# -*- coding: UTF-8 -*-

from enum import Enum
import numpy as np
import os
from itertools import islice  
from apscheduler.schedulers.background import BackgroundScheduler
from common import TradeSide,OrderStatus,printLog

class PriceType(Enum):
    AT        = 'AT'       # 自动
    PRE_CLOSE ="P2"        # 昨收价
    OPEN      ="P3"        # 开盘价
    HIGH      ="P4"        # 最高价
    LOW       ="P5"        # 最低价
    HIGH_LIMIT="P6"        # 涨停价
    LOW_LIMIT ="P7"        # 跌停价
    LAST      ="P8"        # 最新价  
    ASK_1     ="P9"        # 卖一价
    ASK_2     ="P10"   # 卖二价
    ASK_3     ="P11"   # 卖三价
    ASK_4     ="P12"   # 卖四价
    ASK_5     ="P13"   # 卖五价
    ASK_6     ="P14"   # 卖六价
    ASK_7     ="P15"   # 卖七价
    ASK_8     ="P16"   # 卖八价
    ASK_9     ="P17"   # 卖九价
    ASK_10    ="P18"   # 卖十价  
    BID_1     ="P19"   # 买一价
    BID_2     ="P20"   # 买二价
    BID_3     ="P21"   # 买三价
    BID_4     ="P22"   # 买四价
    BID_5     ="P23"   # 买五价
    BID_6     ="P24"   # 买六价
    BID_7     ="P25"   # 买七价
    BID_8     ="P26"   # 买八价
    BID_9     ="P27"   # 买九价
    BID_10    ="P28"   # 买十价

class EexcResultStatus(Enum):
    SENT =   '0'           #已发送
    FAILED = '1'           #失败
    SUCCEED = '2'          #委托成功
    UNKONW  = '3'          #未知，网络超时/柜台超时等

class DMAOrder(object):

    def __int__(self):

        self.custBatchNo = None
        self.acctType   = None
        self.acct       = None
        self.symbol     = None
        self.tradeSide  = None
        self.targetQty  = None
        self.algoId     = None
        self.priceType  = None
        self.ticks      = None
        self.highLimitPrice=0.0
        self.lowLimitPrice = 0.0
        self.updTime    = None

    def parseDMAOrder(self,params):

        self.custBatchNo = params['custBatchNo']
        self.acctType   = params['acctType']
        self.acct       = params['acct']
        self.symbol     = params['symbol']
        self.tradeSide  = params['tradeSide']
        self.targetQty  = params['targetQty']
        self.algoId     = params['algoId']
        self.priceType  = params['priceType']
        self.ticks      = params['ticks']
        self.highLimitPrice=params['highLimitPrice']
        self.lowLimitPrice = params['lowLimitPrice']        
        self.updTime    = params['updTime']

    def toCSVFormat(self):

        order_elements=[self.updTime
                ,self.custBatchNo
                ,self.acctType
                ,self.acct
                ,self.symbol
                ,self.tradeSide
                ,self.targetQty
                ,self.algoId
                ,self.priceType
                ,self.ticks
                ,self.highLimitPrice
                ,self.lowLimitPrice
                ]
        order_str = ",".join([str(ele) for ele in order_elements])
        return order_str

    def toOrderRec(self):

        order = OrderRec()
        order.updTime    = self.updTime
        order.orderDate = None
        order.orderTime = None
        order.acctType   = self.acctType
        order.acct       = self.acct
        order.symbol     = self.symbol
        order.tradeSide  = self.tradeSide
        order.status     = None
        order.orderQty   = self.targetQty
        order.orderPrice = 0.0
        order.orderType  = None
        order.filledQty  = 0
        order.avgPrice   = 0
        order.filledAmt  = 0
        order.cancelQty  = 0
        order.orderNo    = None
        order.corrId     = self.algoId
        order.text       = self.priceType
        order.cliOrderId = None
        return order

    def __str__(self):
        return str(self.__dict__) 
            
class OrderRec(object):

    def __init__(self):

        self.updTime    = None
        self.orderDate = None
        self.orderTime = None 
        self.acctType   = None
        self.acct       = None
        self.symbol     = None
        self.tradeSide  = None
        self.status     = None
        self.orderQty   = None
        self.orderPrice = None  
        self.orderType  = None
        self.filledQty  = None
        self.avgPrice   = None
        self.filledAmt  = None
        self.cancelQty  = None
        self.orderNo    = None
        self.corrId     = None
        self.custBatchNo = None
        self.text        = None
        self.cliOrderId = None

    def parsOrderRec(self,csv_orderRec_str):
        order_elements = csv_orderRec_str.split(",")
        if len(order_elements)>=19:
            self.updTime    = float(order_elements[0])
            self.orderDate = order_elements[1]
            self.orderTime = order_elements[2] 
            self.acctType   = order_elements[3]
            self.acct       = order_elements[4]
            self.symbol     = order_elements[5]
            self.tradeSide  = order_elements[6]
            self.status     = int(order_elements[7])
            self.orderQty   = int(order_elements[8])
            self.orderPrice = order_elements[9] 
            self.orderType  = order_elements[10]
            self.filledQty  = int(order_elements[11])
            self.avgPrice   = float(order_elements[12])
            self.filledAmt  = float(order_elements[13])
            self.cancelQty  = int(order_elements[14])
            self.orderNo    = order_elements[15]
            self.corrId     = order_elements[16]
            self.custBatchNo = order_elements[17]
            self.text        = order_elements[18]
            self.cliOrderId    = order_elements[19]

    def __str__(self):
        return str(self.__dict__)

class EexcResult(object):
    
    def __init__(self):

        self.updTime=None
        self.resultType=None
        self.custBatchNo=None
        self.status=None
        self.errorCode=None
        self.errorMsg=None

    def parseExecResult(self,csv_exec_result_str):
        field_elements = csv_exec_result_str.split(",")
        if len(field_elements)>=6:
            self.updTime    = field_elements[0]
            self.resultType = field_elements[1]
            self.custBatchNo = field_elements[2] 
            self.status      = field_elements[3]
            self.errorCode    = field_elements[4]
            self.errorMsg     = field_elements[5]
    def __str__(self):
        return str(self.__dict__)

class OrderBook(object):
        
    def __init__(self,dma_dir,date):

        self.dma_dir=dma_dir
        self.orderUpdate_path=os.path.join(self.dma_dir,'orderUpdate_' + date + '.csv')
        self.execResult_path=os.path.join(self.dma_dir,'execResult_' + date + '.csv')
        
        self.isRunning=False
        self.dma_orders={}
        self.execResults_order={}
        self.execResults_cancel={}
        self.execResults_order_failed={}
        self.orderRecs={}
        self.positions={}
        
        self.execResult_list=[]
        
        self.orderRecs_last_location=0
        self.execResults_last_location=0
        
        self.symbol2QtySummary={}
        

    def init_history(self):
        if os.path.exists(self.orderUpdate_path):
            with open(self.orderUpdate_path,'r') as f:
                for line in islice(f,1,None):       # 跳过首行字段
                    orderRec = OrderRec()
                    orderRec.parsOrderRec(line.strip())
                    if orderRec.custBatchNo.strip():            # only send by DMA
                        self.orderRecs.update({orderRec.custBatchNo:orderRec})
                    #print(orderRec)
                self.orderRecs_last_location=f.tell()

        if os.path.exists(self.execResult_path):
            with open(self.execResult_path,'r') as f:
                for line in islice(f,1,None):       # 跳过首行字段
                    execr = EexcResult()
                    execr.parseExecResult(line.strip())
                    self.execResult_list.append(execr)
                    if execr.resultType=='1':
                        self.execResults_order.update({execr.custBatchNo:execr})
                        if execr.status == EexcResultStatus.FAILED.value:
                            self.execResults_order_failed.update({execr.custBatchNo:execr})
                            if execr.custBatchNo in self.execResults_order:
                                self.execResults_order.pop(execr.custBatchNo)

                    if execr.resultType=='2':
                        self.execResults_cancel.update({execr.custBatchNo:execr})
                    #print(execr)
                self.execResults_last_location=f.tell()

        printLog("初始化订单簿，母单数量:%d,失败数:%d,子单数:%d,执行响应记录:%d" % (len(self.execResults_order),len(self.execResults_order_failed),len(self.orderRecs),len(self.execResult_list)))
    
    def getQtySummary(self,symbol,tradeSide,acct=None):
        filledQty=0
        effetiveQty=0
        pendingQty=0
        canceledQty=0
        unfinishStatus=OrderStatus.getUnfinishStatus()
        canceledStatus=OrderStatus.getCanceledStatus()
        if acct:
            filledQty = np.sum([order.filledQty                  for custBatchNo,order in self.orderRecs.items() if order.symbol==symbol and order.acct==acct and TradeSide(order.tradeSide)==tradeSide ])
            pendingQty = np.sum([order.orderQty-order.filledQty  for custBatchNo,order in self.orderRecs.items() if (order.symbol==symbol and order.acct==acct and TradeSide(order.tradeSide)==tradeSide and order.status in unfinishStatus) ])
            canceledQty = np.sum([order.orderQty-order.filledQty for custBatchNo,order in self.orderRecs.items() if (order.symbol==symbol and order.acct==acct and TradeSide(order.tradeSide)==tradeSide and order.status in canceledStatus) ])

        else:
            filledQty = np.sum([order.filledQty                   for custBatchNo,order in self.orderRecs.items() if order.symbol==symbol and TradeSide(order.tradeSide)==tradeSide ])
            pendingQty = np.sum([order.orderQty - order.filledQty for custBatchNo,order in self.orderRecs.items() if (order.symbol==symbol and TradeSide(order.tradeSide)==tradeSide and order.status in unfinishStatus) ])
            canceledQty = np.sum([order.orderQty- order.filledQty for custBatchNo,order in self.orderRecs.items() if (order.symbol==symbol and TradeSide(order.tradeSide)==tradeSide and order.status in canceledStatus) ])

        effetiveQty =  filledQty + pendingQty  + canceledQty          
        return (int(effetiveQty),int(filledQty),int(pendingQty),int(canceledQty))
    
    def getUnfinishOrders(self,symbol,tradeSide,acct=None):

        status= OrderStatus.getUnfinishStatus()
        result=[]
        if acct:
            result = [order for _,order in self.orderRecs.items() if (order.symbol==symbol and TradeSide(order.tradeSide)==tradeSide and order.status in status and order.acct==acct)]
        else:
            result = [order for _,order in self.orderRecs.items() if (order.symbol==symbol and TradeSide(order.tradeSide)==tradeSide and order.status in status)]
        return result

    def getAllOrders(self,symbol,tradeSide,acct=None):

        result=[]
        if acct:
            result = [order for _,order in self.orderRecs.items() if (order.symbol==symbol and TradeSide(order.tradeSide)==tradeSide and order.acct==acct)]
        else:
            result = [order for _,order in self.orderRecs.items() if (order.symbol==symbol and TradeSide(order.tradeSide)==tradeSide )]
        return result
        
    def getOrderRecs(self,custBatchNo):
        return self.orderRecs.get(custBatchNo,None)

    def addDMAOrder(self,dmaOrder):
        self.dma_orders[dmaOrder.custBatchNo]=dmaOrder

    def read_orderUpdates(self):
        if os.path.exists(self.orderUpdate_path):
            with open(self.orderUpdate_path,'r') as f:
                f.seek(self.orderRecs_last_location)
                beginline=0
                if self.orderRecs_last_location==0:     # 跳过首行字段,如init_history执行时,orderUpdate_path文件尚不存在，这里要跳过首行字段
                    beginline=1
                lines=islice(f,beginline,None)
                if self.isRunning:
                    for line in lines:     #读增量内容
                        if len(line.strip())>=2:
                            orderRec = OrderRec()
                            orderRec.parsOrderRec(line.strip())
                            if orderRec.custBatchNo.strip():            # only send by DMA
                                self.orderRecs.update({orderRec.custBatchNo:orderRec})
                    cur_location = f.tell()        
                    self.orderRecs_last_location = cur_location

    def read_execResults(self):
        if os.path.exists(self.execResult_path):
            with open(self.execResult_path,'r') as f:
                f.seek(self.execResults_last_location)
                beginline=0
                if self.execResults_last_location==0:     # 跳过首行字段
                    beginline=1
                lines=islice(f,beginline,None)
                if self.isRunning:
                    for line in lines:     #读增量内容
                        if len(line.strip())>=2:
                            execr = EexcResult()
                            execr.parseExecResult(line.strip())
                            self.execResult_list.append(execr)
                            if execr.resultType=='1':
                                self.execResults_order.update({execr.custBatchNo:execr})
                                if execr.status == EexcResultStatus.FAILED.value:
                                    self.execResults_order_failed.update({execr.custBatchNo:execr})
                                    if execr.custBatchNo in self.execResults_order:
                                        self.execResults_order.pop(execr.custBatchNo)
                            if execr.resultType=='2':
                                self.execResults_cancel.update({execr.custBatchNo:execr})
                    cur_location = f.tell()        
                    self.execResults_last_location = cur_location
                    
    def run(self):
        # 在此实现 增量读取订单发送响应结果文件:execResult_path 
        # 实现 增量读取订单回报文件       :orderUpdate_path 
        # 实现 读取仓位信息 等等 其他回写文件等
        self.isRunning=True
        self.scheduler = BackgroundScheduler()
        self.scheduler.add_job(self.read_orderUpdates,'interval', seconds=2,max_instances=1,id='id_read_orderUpdates',coalesce =True)
        self.scheduler.add_job(self.read_execResults ,'interval', seconds=2,max_instances=1,id='id_read_execResults' ,coalesce =True)        
        self.scheduler.start()
        
    def stop(self):
        self.isRunning=False
        # self.scheduler.remove_job('id_read_orderUpdates')
        # self.scheduler.remove_job('id_read_execResults')
        self.scheduler.shutdown(wait=False)
        printLog("订单簿停止刷新，当前母单数量:%d,失败数:%d,子单数:%d,执行响应记录:%d" % (len(self.execResults_order),len(self.execResults_order_failed),len(self.orderRecs),len(self.execResult_list)))
        printLog("OrderBook stop reading flies in " + self.dma_dir)